package com.gxs.tga.poc.rest;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.apache.commons.lang.StringUtils;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.gxs.tga.poc.domain.TgaDataCard;
import com.gxs.tga.poc.domain.exceptions.DuplicateTgaDataCard;
import com.gxs.tga.poc.domain.exceptions.InvalidClientRequest;
import com.gxs.tga.poc.rest.common.TgaRestResponse;
import com.gxs.tga.poc.service.TgaDataCardService;

@Component("tgaDataCardRestBean")
@Path("/dataCards")
public class TgaDataCardRestBean
{
	@Autowired
	private TgaDataCardService tgaDataCardService;

	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public Response getAllDataCards(@QueryParam("shellId") String shellId)
	{

		try
		{
			List<TgaDataCard> list;
			
			if(shellId == null)
				list = this.tgaDataCardService.findAll();
			else
				list = this.tgaDataCardService.findByShellId(shellId);
			
			List<JSONObject> listOfDataCards = new ArrayList<JSONObject>();
			
			for(TgaDataCard card: list)
			{			
				listOfDataCards.add(getJsonRepresentationForCardEntry(card));
			}
			
			return Response.status(Status.OK).entity(listOfDataCards).build();
		}
		catch(Exception e)
		{
			e.printStackTrace();
			int statusCode = 500;
			TgaRestResponse response = new TgaRestResponse(statusCode, "Error Occurred: " + e.getMessage(), statusCode);
			return Response.status(statusCode).entity(response).build();
		}
	}

	private JSONObject getJsonRepresentationForCardEntry(TgaDataCard card) throws ParseException
	{
		String jsonString = new String(card.getDatacardBlob());
		JSONObject json = (JSONObject)new JSONParser().parse(jsonString);
		
		return json;
	}

	@GET
	@Path("/{sheetId}")
	@Produces(MediaType.APPLICATION_JSON)
	public Response getLatestSheetByName(@PathParam("sheetId") String sheetId) throws ParseException
	{
		List<TgaDataCard> listOfCads = this.tgaDataCardService.findBySheetId(sheetId);
		
		if (listOfCads == null || listOfCads.size() < 1)
		{
			int statusCode = 404;
			TgaRestResponse response = new TgaRestResponse(statusCode, "No Data Card exists with sheet id: " + sheetId, statusCode);
			return Response.status(statusCode).entity(response).build();
		} else
		{
			// Note: 
			// We really should put the Max(Version) qualifier in our query - but doing manual parsing as a temporary tactic. 
			if(listOfCads.size() == 1)
			{
				JSONObject objectToReturn = getJsonRepresentationForCardEntry(listOfCads.get(0) );
				return Response.status(200).entity(objectToReturn).build();
			}
			else
			{
				double previousVersion = 0.0;
				TgaDataCard cardToReturn = null;
				for(TgaDataCard tempCard: listOfCads )
				{
					double currentVersion = Double.parseDouble(tempCard.getVersion() );
					if(currentVersion > previousVersion)
					{
						previousVersion = currentVersion;
						cardToReturn = tempCard;
					}
				}
				
				JSONObject objectToReturn = getJsonRepresentationForCardEntry(cardToReturn);
				return Response.status(200).entity(objectToReturn).build();

			}
			
		}

	}

	@POST
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public Response createOrUpdateDataCard(JSONObject jsonObject, @QueryParam("operation") String operation) throws InvalidClientRequest, ParseException 
	{
		
		if(operation == null )
		{
			throw new InvalidClientRequest("Missing Query Parameter: 'operation' -- must be provided. Valid Values are: [create, update]");
		}
		else if(operation.equals("create") || operation.equals("update"))
		{
			// good
		}
		else
		{
			throw new InvalidClientRequest("Incorrect value for Query Parameter: 'operation' -- must be provided. Valid Values are: [create, update]");
		}
 
		if(operation.equals("update"))
		{
			throw new InvalidClientRequest("Work In Progress: Update operation is currently in development");
		}
		
		try
		{
			String sheetId = (String) jsonObject.get("sheetId");
			String sheetVersion = (String)  jsonObject.get("version");
			String shellId =  (String)  jsonObject.get("shellId");
			String sheetName = (String) jsonObject.get("sheetName");
			
			if(StringUtils.isEmpty(sheetId))
			{
				throw new InvalidClientRequest("Error: Sheet id cannot be null");
			}
			if(StringUtils.isEmpty(sheetVersion))
			{
				throw new InvalidClientRequest("Error: Sheet version cannot be null");
			}
			if(StringUtils.isEmpty(shellId))
			{
				throw new InvalidClientRequest("Error: Shell id cannot be null");
			}
			
			List<TgaDataCard> existing = this.tgaDataCardService.findBySheetIdAndVersion(sheetId, sheetVersion);
			
			if(existing != null && existing.size() > 0 )
			{
				throw new InvalidClientRequest("Error: A Sheet with this id and vresion already exists. If you wish to update, please use the operation=update flag");
			}
			
			byte [] jsonBlob = jsonObject.toJSONString().getBytes();

			TgaDataCard card = new TgaDataCard();
			card.setActive("Y");
			card.setDatacardBlob(jsonBlob);
			card.setDatacardRecordId(UUID.randomUUID().toString());
			card.setSheetId(sheetId);
			card.setSheetName(sheetName);
			card.setShellId(shellId);
			card.setVersion(sheetVersion);
			
			this.tgaDataCardService.create(card);
			
			int statusCode  = Response.Status.CREATED.getStatusCode();
			JSONObject objectToReturn = getJsonRepresentationForCardEntry(card);
			
			
			TgaRestResponse response = new TgaRestResponse(statusCode, "Data card with sheetId " + card.getSheetId() + " has been created");
			response.setTargetResource(objectToReturn);
			response.setTargetResourceRefId(card.getSheetId());
			response.setTargetResourcePKAttributeName("sheetId");
			
			return Response.status(statusCode).entity(response).build();
		}
		catch(DuplicateTgaDataCard dte)
		{
			int statusCode  = Response.Status.CONFLICT.getStatusCode();
			TgaRestResponse response = new TgaRestResponse(statusCode, "Error: Datacard already exists" , statusCode);
			return Response.status(statusCode).entity(response).build();

		}
		
		
	}
 
}
